﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Web.UI;
using System.Drawing.Drawing2D;

namespace TanashCMS.Business.Utility
{
    public enum FillMethod
    {
        StretchToSize,
        Stretch,
        CropToSize,
        Crop,
        RestrictToSize
    }

    public class Thumbnailer
    {
        private HttpContext oContext = null;
        private int _desiredWidth;
        private int _desiredHeight;
        private FillMethod _fillMethod;
        private string _filePath;
        private string _fileSystemPath;
        private string _missingImagePath;
        private string _missingImageSystemPath;
        private Stream _outputStream;
        private HttpResponse _response;
        private StreamWriter _writer;
        private float _cropCenterX;
        private float _cropCenterY;

        public Thumbnailer(int desiredWidth, int desiredHeight, FillMethod fillMethod, string filePath, string missingImagePath, HttpResponse response, float cropCenterX, float cropCenterY)
        {
            if (string.IsNullOrEmpty(filePath))
                throw new ArgumentNullException("filePath");
            if (string.IsNullOrEmpty(missingImagePath))
                throw new ArgumentNullException("missingImagePath");

            _desiredWidth = desiredWidth;
            _desiredHeight = desiredHeight;
            _fillMethod = fillMethod;
            _cropCenterX = cropCenterX;
            _cropCenterY = cropCenterY;
            _filePath = filePath;
            _fileSystemPath = System.Web.Hosting.HostingEnvironment.MapPath(_filePath);
            _missingImagePath = missingImagePath;
            _missingImageSystemPath = System.Web.Hosting.HostingEnvironment.MapPath(_missingImagePath);
            _response = response;
            _outputStream = response.OutputStream;
            _writer = new StreamWriter(_outputStream);
        }

        private bool ThumbnailCallback()
        {
            return true;
        }

        public void Process()
        {
            try
            {
                if (File.Exists(_fileSystemPath))
                {
                    OutputImage(_fileSystemPath, _desiredWidth, _desiredHeight, _fillMethod);
                }
                else
                {
                    OutputImage(_missingImageSystemPath, _desiredWidth, _desiredHeight, _fillMethod);
                }
            }
            catch (Exception ex)
            {
                _writer.Write(ex.Message+"<br/>");
                _writer.Write(ex.StackTrace + "<br/>");
                while(ex.InnerException != null)
                {
                    ex = ex.InnerException;
                    _writer.Write(ex.Message + "<br/>");
                    _writer.Write(ex.StackTrace + "<br/>");
                }
            }
            _writer.Flush();
            _writer.Close();
        }

        private void OutputImage(string sPath, int desiredWidth, int desiredHeight, FillMethod fillMethod)
        {
            //TODO: shouldn't upscale images
            using (Image oImg = Image.FromFile(sPath))
            {
                Image.GetThumbnailImageAbort myCallback = ThumbnailCallback;
                
                Bitmap myBitmap = new Bitmap(oImg);

                if (desiredHeight <= 1)
                    desiredHeight = Convert.ToInt32((double)myBitmap.Height * ((double)desiredWidth / (double)myBitmap.Width));

                if (desiredWidth <= 1)
                    desiredWidth = Convert.ToInt32((double)myBitmap.Width * ((double)desiredHeight / (double)myBitmap.Height));

                if (fillMethod == FillMethod.Stretch)
                {
                    using (Image myThumbnail = myBitmap.GetThumbnailImage(
                        desiredWidth, Convert.ToInt32((double)myBitmap.Height * (double)desiredWidth / (double)myBitmap.Width), myCallback, IntPtr.Zero))
                    {
                        _response.ContentType = "image/jpeg";
                        myThumbnail.Save(_outputStream, ImageFormat.Jpeg);
                    }
                }
                else if (fillMethod == FillMethod.StretchToSize)
                {
                    using (Image myThumbnail = myBitmap.GetThumbnailImage(
                        desiredWidth, desiredHeight, myCallback, IntPtr.Zero))
                    {
                        _response.ContentType = "image/jpeg";
                        myThumbnail.Save(_outputStream, ImageFormat.Jpeg);
                    }
                }
                else if (fillMethod == FillMethod.RestrictToSize)
                {
                    if (!(myBitmap.Height > desiredHeight || myBitmap.Width > desiredWidth))
                    {
                        desiredHeight = myBitmap.Height;
                        desiredWidth = myBitmap.Width;
                    }
                    using (Image myThumbnail = myBitmap.GetThumbnailImage(
                        desiredWidth, desiredHeight, myCallback, IntPtr.Zero))
                    {
                        _response.ContentType = "image/jpeg";
                        myThumbnail.Save(_outputStream, ImageFormat.Jpeg);
                    }
                }
                else if (fillMethod == FillMethod.CropToSize)
                {
                    if (myBitmap.Width == myBitmap.Height)
                    {
                        using (Image myThumbnail = myBitmap.GetThumbnailImage(
                            desiredWidth, desiredHeight, myCallback, IntPtr.Zero))
                        {
                            _response.ContentType = "image/jpeg";
                            myThumbnail.Save(_outputStream, ImageFormat.Jpeg);
                        }
                    }
                    else if (myBitmap.Width > myBitmap.Height)
                    {
                        // width -> fillMethod to desiredWidth
                        // height -> shrink to desiredHeight
                        using (Image myThumbnail = myBitmap.GetThumbnailImage(
                            myBitmap.Width*desiredHeight/myBitmap.Height, desiredHeight, myCallback, IntPtr.Zero))
                        {
                            Bitmap cropped = new Bitmap(desiredWidth, desiredHeight);
                            Graphics g = CreateGraphics(cropped);
                            g.DrawImage(myThumbnail, new Rectangle(0, 0, desiredWidth, desiredHeight),
                                        (int) ((myThumbnail.Width - desiredWidth)*_cropCenterX), 0, desiredWidth,
                                        desiredHeight, GraphicsUnit.Pixel);
                            g.Dispose();
                            _response.ContentType = "image/jpeg";
                            cropped.Save(_outputStream, ImageFormat.Jpeg);
                        }
                    }
                    else if (myBitmap.Width < myBitmap.Height)
                    {
                        // width -> shrink to desiredWidth
                        // height -> fillMethod to desiredHeight
                        using (Image myThumbnail = myBitmap.GetThumbnailImage(
                            desiredWidth, myBitmap.Height*desiredWidth/myBitmap.Width, myCallback, IntPtr.Zero))
                        {
                            Bitmap cropped = new Bitmap(desiredWidth, desiredHeight);
                            Graphics g = CreateGraphics(cropped);
                            g.DrawImage(myThumbnail, new Rectangle(0, 0, desiredWidth, desiredHeight), 0,
                                        (int) ((myThumbnail.Height-desiredHeight)*_cropCenterY), desiredWidth,
                                        desiredHeight, GraphicsUnit.Pixel);
                            g.Dispose();
                            _response.ContentType = "image/jpeg";
                            cropped.Save(_outputStream, ImageFormat.Jpeg);
                        }
                    }
                } else if (fillMethod == FillMethod.Crop)
                {
                    using (Image myThumbnail = (Image)myBitmap.Clone())
                    {
                        Bitmap cropped = new Bitmap(desiredWidth, desiredHeight);
                        Graphics g = CreateGraphics(cropped);
                        g.DrawImage(myThumbnail, 
                                    new Rectangle(0, 0, myThumbnail.Width, myThumbnail.Height),
                                    (int) ((myThumbnail.Width - desiredWidth) * _cropCenterX),
                                    (int) ((myThumbnail.Height - desiredHeight) * _cropCenterY),
                                    myBitmap.Width, myBitmap.Height, GraphicsUnit.Pixel);
                        g.Dispose();
                        _response.ContentType = "image/jpeg";
                        cropped.Save(_outputStream, ImageFormat.Jpeg);
                    }
                    
                }
            }
        }

        private static Graphics CreateGraphics(Bitmap cropped, bool HighQuality)
        {
            Graphics g = Graphics.FromImage(cropped);
            if (HighQuality)
            {
                g.SmoothingMode = SmoothingMode.HighQuality;
                g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                g.CompositingQuality = CompositingQuality.HighQuality;
            }
            return g;
        }

        private static Graphics CreateGraphics(Bitmap cropped)
        {
            return CreateGraphics(cropped, true);
        }
    }
}